package dp.util;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;


/**
 * 非对称RSA安全编码组件
 */
public class RSAPlus {
	/**RSA算法*/
	private static final String KEY_ALGORITHM = "RSA";
	/**RSA公钥*/
	private static final String PUBLIC_KEY = "RSAPublicKey";
	/**RSA私钥*/
	private static final String PRIVATE_KEY = "RSAPrivateKey";
	/*RSA加密明文最大长度117字节，解密要求密文最大长度为128字节，所以在加密和解密的过程中需要分块进行。*/
	 /** RSA最大加密明文大小 */  
    private static final int MAX_ENCRYPT_BLOCK = 117;  
    /**  RSA最大解密密文大小 */  
    private static final int MAX_DECRYPT_BLOCK = 128;
    
    //公钥加密解密Cipher
    private Cipher publicKeyEncryptCipher = null;
    private Cipher publicKeyDecryptCipher = null;
    //私钥加密解密Cipher
    private Cipher privateKeyEncryptCipher = null;
    private Cipher privateKeyDecryptCipher = null;
    
    public RSAPlus(String publicKeyStr, String privateKeyStr){
    	try{
	    	// 公钥
	    	if(StringUtils.isNotBlank(publicKeyStr)){
	    		byte[] keyBytes = Base64.decodeBase64(publicKeyStr);
	    		// 取得公钥
	    		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
	    		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
	    		Key publicKey = keyFactory.generatePublic(x509KeySpec);
	    		// 加密Cipher
	    		publicKeyEncryptCipher = Cipher.getInstance(keyFactory.getAlgorithm());
	    		publicKeyEncryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
	    		// 解密Cipher
	    		publicKeyDecryptCipher = Cipher.getInstance(keyFactory.getAlgorithm());
	    		publicKeyDecryptCipher.init(Cipher.DECRYPT_MODE, publicKey);
	    	}
	    	
	    	//私钥
	    	if(StringUtils.isNotBlank(privateKeyStr)){
				byte[] keyBytes = Base64.decodeBase64(privateKeyStr);
				// 取得私钥
				PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
				KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
				Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
				// 加密Cipher
				privateKeyEncryptCipher = Cipher.getInstance(keyFactory.getAlgorithm());
				privateKeyEncryptCipher.init(Cipher.ENCRYPT_MODE, privateKey);
				// 解密Cipher
				privateKeyDecryptCipher = Cipher.getInstance(keyFactory.getAlgorithm());
				privateKeyDecryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
	    	}
    	} catch (Exception e) {
			throw new RuntimeException(e);
		}
    }

	/**
	 * 初始化密钥，生成公钥和私钥对
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Key> initKey(){
		try{
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
			keyPairGen.initialize(1024);
			KeyPair keyPair = keyPairGen.generateKeyPair();
			// 公钥
			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
			// 私钥
			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
			Map<String, Key> keyMap = new HashMap<String, Key>(2);
			keyMap.put(PUBLIC_KEY, publicKey);
			keyMap.put(PRIVATE_KEY, privateKey);
			return keyMap;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 取得私钥，并用BASE64编码
	 * @param keyMap 密钥对Map
	 * @return
	 * @throws Exception
	 */
	public static String getPrivateKey(Map<String, Key> keyMap) {
		try{
			Key key = (Key) keyMap.get(PRIVATE_KEY);
			return Base64.encodeBase64String(key.getEncoded());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 取得公钥,并用BASE64编码
	 * @param keyMap 密钥对Map
	 * @return
	 * @throws Exception
	 */
	public static String getPublicKey(Map<String, Key> keyMap){
		try{
			Key key = (Key) keyMap.get(PUBLIC_KEY);
			return Base64.encodeBase64String(key.getEncoded());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * RSA公钥加密
	 * @param dataStr 加密数据
	 * @param key RSA公钥
	 * @return 公钥加密后的字符串
	 * @throws Exception
	 */
	public String encryptByPublicKey(String dataStr) {
		byte[] sourceBytes = dataStr.getBytes();
		int inputLen = sourceBytes.length; 
		ByteArrayOutputStream out = null;
		try{
	        out = new ByteArrayOutputStream();  
	        int offSet = 0;  
	        byte[] cache;  
	        int i = 0;  
	        // 对数据分段加密  
	        while (inputLen - offSet > 0) {  
	            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {  
	                cache = publicKeyEncryptCipher.doFinal(sourceBytes, offSet, MAX_ENCRYPT_BLOCK);  
	            } else {  
	                cache = publicKeyEncryptCipher.doFinal(sourceBytes, offSet, inputLen - offSet);  
	            }  
	            out.write(cache, 0, cache.length);  
	            i++;  
	            offSet = i * MAX_ENCRYPT_BLOCK;  
	        }  
		}catch(Exception e){
			throw new RuntimeException(e.getMessage());
		}finally{
			IOUtils.closeQuietly(out); 
		}
//        return ByteUtil.byteArr2HexStr(out.toByteArray());
		return Base64.encodeBase64String(out.toByteArray());
	}
	
	/**
	 * 公钥解密
	 */
	public String decryptByPublicKey(String dataStr){
//		byte[] sourceBytes = ByteUtil.hexStr2ByteArr(dataStr);
		byte[] sourceBytes = Base64.decodeBase64(dataStr);
		int inputLen = sourceBytes.length;
		ByteArrayOutputStream out =null;
		try{
	        out = new ByteArrayOutputStream();  
	        int offSet = 0;  
	        byte[] cache;  
	        int i = 0;  
	        // 对数据分段解密  
	        while (inputLen - offSet > 0) {  
	            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {  
	                cache = publicKeyDecryptCipher.doFinal(sourceBytes, offSet, MAX_DECRYPT_BLOCK);  
	            } else {  
	                cache = publicKeyDecryptCipher.doFinal(sourceBytes, offSet, inputLen - offSet);  
	            }  
	            out.write(cache, 0, cache.length);  
	            i++;  
	            offSet = i * MAX_DECRYPT_BLOCK;  
	        } 
		}catch(Exception e){
			throw new RuntimeException(e.getMessage());
		}finally{
			IOUtils.closeQuietly(out); 
		}
		return out.toString();
	}
	
	/**
	 * 私钥加密
	 */
	public String encryptByPrivateKey(String dataStr){
		byte[] sourceBytes = dataStr.getBytes();
		int inputLen = sourceBytes.length; 
		ByteArrayOutputStream out = null;
		try{
	        out = new ByteArrayOutputStream();  
	        int offSet = 0;  
	        byte[] cache;  
	        int i = 0;  
	        // 对数据分段加密  
	        while (inputLen - offSet > 0) {  
	            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {  
	                cache = privateKeyEncryptCipher.doFinal(sourceBytes, offSet, MAX_ENCRYPT_BLOCK);  
	            } else {  
	                cache = privateKeyEncryptCipher.doFinal(sourceBytes, offSet, inputLen - offSet);  
	            }  
	            out.write(cache, 0, cache.length);  
	            i++;  
	            offSet = i * MAX_ENCRYPT_BLOCK;  
	        }  
		}catch(Exception e){
			throw new RuntimeException(e.getMessage());
		}finally{
			IOUtils.closeQuietly(out);
		}
//        return ByteUtil.byteArr2HexStr(out.toByteArray());
		return Base64.encodeBase64String(out.toByteArray());
	}

	/**
	 * RSA私钥解密
	 * @param dataStr RSA公钥加密后数据
	 * @param key RSA密钥
	 * @return 原始字符串
	 * @throws Exception
	 */
	public String decryptByPrivateKey(String dataStr) {
//		byte[] sourceBytes = ByteUtil.hexStr2ByteArr(dataStr);
		byte[] sourceBytes = Base64.decodeBase64(dataStr);
		int inputLen = sourceBytes.length;
		ByteArrayOutputStream out =null;
		try{
	        out = new ByteArrayOutputStream();  
	        int offSet = 0;  
	        byte[] cache;  
	        int i = 0;  
	        // 对数据分段解密  
	        while (inputLen - offSet > 0) {  
	            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {  
	                cache = privateKeyDecryptCipher.doFinal(sourceBytes, offSet, MAX_DECRYPT_BLOCK);  
	            } else {  
	                cache = privateKeyDecryptCipher.doFinal(sourceBytes, offSet, inputLen - offSet);  
	            }  
	            out.write(cache, 0, cache.length);  
	            i++;  
	            offSet = i * MAX_DECRYPT_BLOCK;  
	        } 
		}catch(Exception e){
			throw new RuntimeException(e.getMessage());
		}finally{
			IOUtils.closeQuietly(out);
		}
		return out.toString();
	}
	
	public static void main(String[] args) {
		Map<String, Key> keys = RSAPlus.initKey();
		String publicKey = RSAPlus.getPublicKey(keys);
		String privateKey = RSAPlus.getPrivateKey(keys);
		System.out.println("publicKey:"+publicKey);
		System.out.println("privateKey:"+privateKey);
		
		String text = "楼房卡上的放老卡就开饭h地方豆腐干梵蒂冈豆腐sdfsdfsdgsgsdgsafsas放松放松" +
				"斯蒂芬森个豆腐干大概岁的个豆腐干干豆腐干大概广覆盖多个地方规定地方规定郭德纲的郭德纲梵蒂冈啦sdfghssdcx's'z'c";
		
		long ss = System.nanoTime();
		RSAPlus plus = new RSAPlus(publicKey, privateKey);
		String t1,t2;
//		t1 = plus.encryptByPrivateKey(text);
//		for (int i = 0; i < 10000; i++) {
//			
//			t2 = plus.decryptByPublicKey(t1);
//		}
//		System.out.println((System.nanoTime() - ss) / 1000000);
		
		System.out.println("================================私钥加密----公钥解密测试=====================================");
		System.out.println("加密前数据:"+text);
		String str1 = plus.encryptByPrivateKey(text);
		System.out.println("私钥加密后:"+str1);
		String str2 = plus.decryptByPublicKey(str1);
		System.out.println("公钥解密后:"+str2);
		
		System.out.println("================================公钥加密----私钥解密测试=====================================");
		System.out.println("加密前数据:"+text);
		String str3 = plus.encryptByPublicKey(text);
		System.out.println("公钥加密后:"+str3);
		String str4 = plus.decryptByPrivateKey(str3);
		System.out.println("私钥解密后:"+str4);
	}
}

